From f4ebb9ef653c2b92db2166070cbc590efd0dea6a Mon Sep 17 00:00:00 2001 From: tsteven4 <13596209+tsteven4@users.noreply.github.com> Date: Fri, 1 May 2020 07:03:31 -0600 Subject: [PATCH] convert kml to Format class (#550) * convert kml to Format class. * update dependencies. * restore kml real time postion writer functionality. and have it produce valid kml. Add a test case. * correct new kml test reference file name. --- CMakeLists.txt | 1 + GPSBabel.pro | 1 + Makefile.in | 60 +-- kml.cc | 569 +++++++++-------------------- kml.h | 348 ++++++++++++++++++ reference/realtime.kml | 803 +++++++++++++++++++++++++++++++++++++++++ testo.d/kml.test | 4 + vecs.h | 5 +- 8 files changed, 1364 insertions(+), 427 deletions(-) create mode 100644 kml.h create mode 100644 reference/realtime.kml diff --git a/CMakeLists.txt b/CMakeLists.txt index eb17c6561..7e2841e19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,6 +149,7 @@ set(HEADERS jeeps/gpsusbcommon.h jeeps/gpsusbint.h jeeps/gpsutil.h + kml.h legacyformat.h lowranceusr.h magellan.h diff --git a/GPSBabel.pro b/GPSBabel.pro index a2316cd4b..666ca103c 100644 --- a/GPSBabel.pro +++ b/GPSBabel.pro @@ -134,6 +134,7 @@ HEADERS = \ jeeps/gpsusbcommon.h \ jeeps/gpsusbint.h \ jeeps/gpsutil.h \ + kml.h \ legacyformat.h \ lowranceusr.h \ magellan.h \ diff --git a/Makefile.in b/Makefile.in index 4d39de46f..17237f7a4 100644 --- a/Makefile.in +++ b/Makefile.in @@ -510,12 +510,13 @@ filter_vecs.o: filter_vecs.cc defs.h config.h zlib/zlib.h zlib/zconf.h \ swapdata.h trackfilter.h transform.h validate.h gbversion.h vecs.h \ format.h energympro.h garmin_fit.h geojson.h src/core/file.h ggv_bin.h \ globalsat_sport.h gpx.h src/core/xmlstreamwriter.h src/core/xmltag.h \ - legacyformat.h lowranceusr.h mynav.h qstarz_bl_1000.h nmea.h random.h \ - shape.h shapelib/shapefil.h subrip.h xcsv.h garmin_fs.h jeeps/gps.h \ - jeeps/../defs.h jeeps/gpsport.h jeeps/gpsdevice.h jeeps/gpssend.h \ - jeeps/gpsread.h jeeps/gpsutil.h jeeps/gpsapp.h jeeps/gpsprot.h \ - jeeps/gpscom.h jeeps/gpsfmt.h jeeps/gpsmath.h jeeps/gpsmem.h \ - jeeps/gpsrqst.h src/core/textstream.h yahoo.h xmlgeneric.h + kml.h xmlgeneric.h legacyformat.h lowranceusr.h mynav.h nmea.h \ + qstarz_bl_1000.h random.h shape.h shapelib/shapefil.h subrip.h xcsv.h \ + garmin_fs.h jeeps/gps.h jeeps/../defs.h jeeps/gpsport.h \ + jeeps/gpsdevice.h jeeps/gpssend.h jeeps/gpsread.h jeeps/gpsutil.h \ + jeeps/gpsapp.h jeeps/gpsprot.h jeeps/gpscom.h jeeps/gpsfmt.h \ + jeeps/gpsmath.h jeeps/gpsmem.h jeeps/gpsrqst.h src/core/textstream.h \ + yahoo.h formspec.o: formspec.cc defs.h config.h zlib/zlib.h zlib/zconf.h \ formspec.h inifile.h gbfile.h session.h src/core/datetime.h \ src/core/optional.h @@ -535,9 +536,9 @@ garmin.o: garmin.cc defs.h config.h zlib/zlib.h zlib/zconf.h formspec.h \ jeeps/gpsrqst.h garmin_tables.h grtcirc.h jeeps/gpsserial.h vecs.h \ energympro.h garmin_fit.h geojson.h src/core/file.h ggv_bin.h \ globalsat_sport.h gpx.h src/core/xmlstreamwriter.h src/core/xmltag.h \ - legacyformat.h lowranceusr.h mynav.h qstarz_bl_1000.h nmea.h random.h \ - shape.h shapelib/shapefil.h subrip.h xcsv.h src/core/textstream.h \ - yahoo.h xmlgeneric.h + kml.h xmlgeneric.h legacyformat.h lowranceusr.h mynav.h nmea.h \ + qstarz_bl_1000.h random.h shape.h shapelib/shapefil.h subrip.h xcsv.h \ + src/core/textstream.h yahoo.h garmin_device_xml.o: garmin_device_xml.cc defs.h config.h zlib/zlib.h \ zlib/zconf.h formspec.h inifile.h gbfile.h session.h \ src/core/datetime.h src/core/optional.h garmin_device_xml.h \ @@ -806,8 +807,8 @@ jtr.o: jtr.cc defs.h config.h zlib/zlib.h zlib/zconf.h formspec.h \ csv_util.h nmea.h format.h kml.o: kml.cc defs.h config.h zlib/zlib.h zlib/zconf.h formspec.h \ inifile.h gbfile.h session.h src/core/datetime.h src/core/optional.h \ - grtcirc.h src/core/file.h src/core/logging.h \ - src/core/xmlstreamwriter.h src/core/xmltag.h units.h xmlgeneric.h + kml.h format.h src/core/file.h src/core/xmlstreamwriter.h xmlgeneric.h \ + grtcirc.h src/core/logging.h src/core/xmltag.h units.h lmx.o: lmx.cc defs.h config.h zlib/zlib.h zlib/zconf.h formspec.h \ inifile.h gbfile.h session.h src/core/datetime.h src/core/optional.h \ xmlgeneric.h @@ -822,12 +823,13 @@ magproto.o: magproto.cc defs.h config.h zlib/zlib.h zlib/zconf.h \ src/core/optional.h explorist_ini.h format.h gbser.h magellan.h vecs.h \ energympro.h garmin_fit.h geojson.h src/core/file.h ggv_bin.h \ globalsat_sport.h gpx.h src/core/xmlstreamwriter.h src/core/xmltag.h \ - legacyformat.h lowranceusr.h mynav.h qstarz_bl_1000.h nmea.h random.h \ - shape.h shapelib/shapefil.h subrip.h xcsv.h garmin_fs.h jeeps/gps.h \ - jeeps/../defs.h jeeps/gpsport.h jeeps/gpsdevice.h jeeps/gpssend.h \ - jeeps/gpsread.h jeeps/gpsutil.h jeeps/gpsapp.h jeeps/gpsprot.h \ - jeeps/gpscom.h jeeps/gpsfmt.h jeeps/gpsmath.h jeeps/gpsmem.h \ - jeeps/gpsrqst.h src/core/textstream.h yahoo.h xmlgeneric.h + kml.h xmlgeneric.h legacyformat.h lowranceusr.h mynav.h nmea.h \ + qstarz_bl_1000.h random.h shape.h shapelib/shapefil.h subrip.h xcsv.h \ + garmin_fs.h jeeps/gps.h jeeps/../defs.h jeeps/gpsport.h \ + jeeps/gpsdevice.h jeeps/gpssend.h jeeps/gpsread.h jeeps/gpsutil.h \ + jeeps/gpsapp.h jeeps/gpsprot.h jeeps/gpscom.h jeeps/gpsfmt.h \ + jeeps/gpsmath.h jeeps/gpsmem.h jeeps/gpsrqst.h src/core/textstream.h \ + yahoo.h main.o: main.cc defs.h config.h zlib/zlib.h zlib/zconf.h formspec.h \ inifile.h gbfile.h session.h src/core/datetime.h src/core/optional.h \ cet_util.h csv_util.h filter.h filter_vecs.h arcdist.h bend.h \ @@ -836,13 +838,13 @@ main.o: main.cc defs.h config.h zlib/zlib.h zlib/zconf.h formspec.h \ stackfilter.h swapdata.h trackfilter.h transform.h validate.h format.h \ src/core/file.h src/core/usasciicodec.h vecs.h energympro.h \ garmin_fit.h geojson.h ggv_bin.h globalsat_sport.h gpx.h \ - src/core/xmlstreamwriter.h src/core/xmltag.h legacyformat.h \ - lowranceusr.h mynav.h qstarz_bl_1000.h nmea.h random.h shape.h \ - shapelib/shapefil.h subrip.h xcsv.h garmin_fs.h jeeps/gps.h \ + src/core/xmlstreamwriter.h src/core/xmltag.h kml.h xmlgeneric.h \ + legacyformat.h lowranceusr.h mynav.h nmea.h qstarz_bl_1000.h random.h \ + shape.h shapelib/shapefil.h subrip.h xcsv.h garmin_fs.h jeeps/gps.h \ jeeps/../defs.h jeeps/gpsport.h jeeps/gpsdevice.h jeeps/gpssend.h \ jeeps/gpsread.h jeeps/gpsutil.h jeeps/gpsapp.h jeeps/gpsprot.h \ jeeps/gpscom.h jeeps/gpsfmt.h jeeps/gpsmath.h jeeps/gpsmem.h \ - jeeps/gpsrqst.h src/core/textstream.h yahoo.h xmlgeneric.h + jeeps/gpsrqst.h src/core/textstream.h yahoo.h mapasia.o: mapasia.cc defs.h config.h zlib/zlib.h zlib/zconf.h formspec.h \ inifile.h gbfile.h session.h src/core/datetime.h src/core/optional.h mapbar_track.o: mapbar_track.cc defs.h config.h zlib/zlib.h zlib/zconf.h \ @@ -996,7 +998,7 @@ stmsdf.o: stmsdf.cc defs.h config.h zlib/zlib.h zlib/zconf.h formspec.h \ src/core/logging.h stmwpp.o: stmwpp.cc defs.h config.h zlib/zlib.h zlib/zconf.h formspec.h \ inifile.h gbfile.h session.h src/core/datetime.h src/core/optional.h \ - csv_util.h cet_util.h + cet_util.h csv_util.h strptime.o: strptime.c config.h strptime.h subrip.o: subrip.cc defs.h config.h zlib/zlib.h zlib/zconf.h formspec.h \ inifile.h gbfile.h session.h src/core/datetime.h src/core/optional.h \ @@ -1059,13 +1061,13 @@ vecs.o: vecs.cc defs.h config.h zlib/zlib.h zlib/zconf.h formspec.h \ inifile.h gbfile.h session.h src/core/datetime.h src/core/optional.h \ vecs.h format.h energympro.h garmin_fit.h geojson.h src/core/file.h \ ggv_bin.h globalsat_sport.h gpx.h src/core/xmlstreamwriter.h \ - src/core/xmltag.h legacyformat.h lowranceusr.h mynav.h \ - qstarz_bl_1000.h nmea.h random.h shape.h shapelib/shapefil.h subrip.h \ - xcsv.h garmin_fs.h jeeps/gps.h jeeps/../defs.h jeeps/gpsport.h \ - jeeps/gpsdevice.h jeeps/gpssend.h jeeps/gpsread.h jeeps/gpsutil.h \ - jeeps/gpsapp.h jeeps/gpsprot.h jeeps/gpscom.h jeeps/gpsfmt.h \ - jeeps/gpsmath.h jeeps/gpsmem.h jeeps/gpsrqst.h src/core/textstream.h \ - yahoo.h xmlgeneric.h gbversion.h src/core/logging.h + src/core/xmltag.h kml.h xmlgeneric.h legacyformat.h lowranceusr.h \ + mynav.h nmea.h qstarz_bl_1000.h random.h shape.h shapelib/shapefil.h \ + subrip.h xcsv.h garmin_fs.h jeeps/gps.h jeeps/../defs.h \ + jeeps/gpsport.h jeeps/gpsdevice.h jeeps/gpssend.h jeeps/gpsread.h \ + jeeps/gpsutil.h jeeps/gpsapp.h jeeps/gpsprot.h jeeps/gpscom.h \ + jeeps/gpsfmt.h jeeps/gpsmath.h jeeps/gpsmem.h jeeps/gpsrqst.h \ + src/core/textstream.h yahoo.h gbversion.h src/core/logging.h vidaone.o: vidaone.cc defs.h config.h zlib/zlib.h zlib/zconf.h formspec.h \ inifile.h gbfile.h session.h src/core/datetime.h src/core/optional.h vitosmt.o: vitosmt.cc defs.h config.h zlib/zlib.h zlib/zconf.h formspec.h \ diff --git a/kml.cc b/kml.cc index dfaaef939..83c0d18e9 100644 --- a/kml.cc +++ b/kml.cc @@ -47,6 +47,7 @@ #include // for foreach, qint64, qPrintable #include "defs.h" +#include "kml.h" #include "formspec.h" // for FsChainFind, kFsGpx #include "grtcirc.h" // for RAD, gcdist, radtometers #include "src/core/datetime.h" // for DateTime @@ -59,160 +60,8 @@ #include "xmlgeneric.h" // for cb_cdata, cb_end, cb_start, xg_callback, xg_string, xg_cb_type, xml_deinit, xml_ignore_tags, xml_init, xml_read, xg_tag_mapping -// options -static char* opt_deficon = nullptr; -static char* opt_export_lines = nullptr; -static char* opt_export_points = nullptr; -static char* opt_export_track = nullptr; -static char* opt_line_width = nullptr; -static char* opt_line_color = nullptr; -static char* opt_floating = nullptr; -static char* opt_extrude = nullptr; -static char* opt_trackdata = nullptr; -static char* opt_trackdirection = nullptr; -static char* opt_units = nullptr; -static char* opt_labels = nullptr; -static char* opt_max_position_points = nullptr; -static char* opt_rotate_colors = nullptr; -static char* opt_precision = nullptr; - -static int export_lines; -static int export_points; -static int export_track; -static int floating; -static int extrude; -static int trackdata; -static int trackdirection; -static int max_position_points; -static int rotate_colors; -static int line_width; -static int html_encrypt; -static int precision; - -static Waypoint* wpt_tmp; -static int wpt_tmp_queued; -static QString posnfilename; -static QString posnfilenametmp; - -static route_head* gx_trk_head; -static QList* gx_trk_times; -static QList>* gx_trk_coords; - -static gpsbabel::File* oqfile; -static gpsbabel::XmlStreamWriter* writer; - -enum kml_point_type { - kmlpt_unknown, - kmlpt_waypoint, - kmlpt_track, - kmlpt_route, - kmlpt_multitrack, - kmlpt_other -}; - -static int realtime_positioning; -static bounds kml_bounds; -static gpsbabel::DateTime kml_time_max; -static gpsbabel::DateTime kml_time_min; - -#define DEFAULT_PRECISION "6" - // Icons provided and hosted by Google. Used with permission. #define ICON_BASE "https://earth.google.com/images/kml-icons/" - -// Multitrack ids to correlate Schema to SchemaData -static const char kmt_heartrate[] = "heartrate"; -static const char kmt_cadence[] = "cadence"; -static const char kmt_temperature[] = "temperature"; -static const char kmt_depth[] = "depth"; -static const char kmt_power[] = "power"; - - -static -QVector kml_args = { - {"deficon", &opt_deficon, "Default icon name", nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr }, - { - "lines", &opt_export_lines, - "Export linestrings for tracks and routes", - "1", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr, - }, - { - "points", &opt_export_points, - "Export placemarks for tracks and routes", - "1", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr - }, - { - "line_width", &opt_line_width, - "Width of lines, in pixels", - "6", ARGTYPE_INT, ARG_NOMINMAX, nullptr - }, - { - "line_color", &opt_line_color, - "Line color, specified in hex AABBGGRR", - "99ffac59", ARGTYPE_STRING, ARG_NOMINMAX, nullptr - }, - { - "floating", &opt_floating, - "Altitudes are absolute and not clamped to ground", - "0", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr - }, - { - "extrude", &opt_extrude, - "Draw extrusion line from trackpoint to ground", - "0", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr - }, - { - "track", &opt_export_track, - "Write KML track (default = 0)", - "0", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr - }, - { - "trackdata", &opt_trackdata, - "Include extended data for trackpoints (default = 1)", - "1", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr - }, - { - "trackdirection", &opt_trackdirection, - "Indicate direction of travel in track icons (default = 0)", - "0", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr - }, - { - "units", &opt_units, - "Units used when writing comments ('s'tatute, 'm'etric,' 'n'autical, 'a'viation)", - "s", ARGTYPE_STRING, ARG_NOMINMAX, nullptr - }, - { - "labels", &opt_labels, - "Display labels on track and routepoints (default = 1)", - "1", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr - }, - { - "max_position_points", &opt_max_position_points, - "Retain at most this number of position points (0 = unlimited)", - "0", ARGTYPE_INT, ARG_NOMINMAX, nullptr - }, - { - "rotate_colors", &opt_rotate_colors, - "Rotate colors for tracks and routes (default automatic)", - nullptr, ARGTYPE_FLOAT, "0", "360", nullptr - }, - { - "prec", &opt_precision, - "Precision of coordinates, number of decimals", - DEFAULT_PRECISION, ARGTYPE_INT, ARG_NOMINMAX, nullptr - }, -}; - -static -struct { - int freshness; - const char* icon; -} kml_tracking_icons[] = { - { 60, ICON_BASE "youarehere-60.png" }, // Red - { 30, ICON_BASE "youarehere-30.png" }, // Yellow - { 0, ICON_BASE "youarehere-0.png" }, // Green -}; - #define ICON_NOSAT ICON_BASE "youarehere-warning.png" #define ICON_WPT "https://maps.google.com/mapfiles/kml/pal4/icon61.png" #define ICON_TRK ICON_BASE "track-directional/track-none.png" @@ -220,51 +69,44 @@ struct { #define ICON_MULTI_TRK ICON_BASE "track-directional/track-0.png" #define ICON_DIR ICON_BASE "track-directional/track-%1.png" // format string where next arg is rotational degrees. -static struct { - float seq{0.0f}; - float step{0.0f}; - gb_color color; -} kml_color_sequencer; -#define KML_COLOR_LIMIT 204 /* allowed range [0,255] */ - #define MYNAME "kml" -static void kml_init_color_sequencer(unsigned int steps_per_rev) +void KmlFormat::kml_init_color_sequencer(unsigned int steps_per_rev) { if (rotate_colors) { float color_step = atof(opt_rotate_colors); if (color_step > 0.0f) { // step around circle by given number of degrees for each track(route) - kml_color_sequencer.step = ((float)KML_COLOR_LIMIT) * 6.0f * color_step / 360.0f; + kml_color_sequencer.step = ((float)kml_color_limit) * 6.0f * color_step / 360.0f; } else { // one cycle around circle for all the tracks(routes) - kml_color_sequencer.step = ((float)KML_COLOR_LIMIT) * 6.0f / ((float)steps_per_rev); + kml_color_sequencer.step = ((float)kml_color_limit) * 6.0f / ((float)steps_per_rev); } kml_color_sequencer.color.opacity=255; kml_color_sequencer.seq = 0.0f; } } -static void kml_step_color() +void KmlFormat::kml_step_color() { - // Map kml_color_sequencer.seq to an integer in the range [0, KML_COLOR_LIMIT*6). + // Map kml_color_sequencer.seq to an integer in the range [0, kml_color_limit*6). // Note that color_seq may be outside this range if the cast from float to int fails. - int color_seq = ((int) kml_color_sequencer.seq) % (KML_COLOR_LIMIT * 6); + int color_seq = ((int) kml_color_sequencer.seq) % (kml_color_limit * 6); if (global_opts.debug_level >= 1) { printf(MYNAME ": kml_color_sequencer seq %f %d, step %f\n",kml_color_sequencer.seq, color_seq, kml_color_sequencer.step); } - if ((color_seq >= (0*KML_COLOR_LIMIT)) && (color_seq < (1*KML_COLOR_LIMIT))) { - kml_color_sequencer.color.bbggrr = (0)<<16 | (color_seq)<<8 | (KML_COLOR_LIMIT); - } else if ((color_seq >= (1*KML_COLOR_LIMIT)) && (color_seq < (2*KML_COLOR_LIMIT))) { - kml_color_sequencer.color.bbggrr = (0)<<16 | (KML_COLOR_LIMIT)<<8 | (2*KML_COLOR_LIMIT-color_seq); - } else if ((color_seq >= (2*KML_COLOR_LIMIT)) && (color_seq < (3*KML_COLOR_LIMIT))) { - kml_color_sequencer.color.bbggrr = (color_seq-2*KML_COLOR_LIMIT)<<16 | (KML_COLOR_LIMIT)<<8 | (0); - } else if ((color_seq >= (3*KML_COLOR_LIMIT)) && (color_seq < (4*KML_COLOR_LIMIT))) { - kml_color_sequencer.color.bbggrr = (KML_COLOR_LIMIT)<<16 | (4*KML_COLOR_LIMIT-color_seq)<<8 | (0); - } else if ((color_seq >= (4*KML_COLOR_LIMIT)) && (color_seq < (5*KML_COLOR_LIMIT))) { - kml_color_sequencer.color.bbggrr = (KML_COLOR_LIMIT)<<16 | (0)<<8 | (color_seq-4*KML_COLOR_LIMIT); - } else if ((color_seq >= (5*KML_COLOR_LIMIT)) && (color_seq < (6*KML_COLOR_LIMIT))) { - kml_color_sequencer.color.bbggrr = (6*KML_COLOR_LIMIT-color_seq)<<16 | (0)<<8 | (KML_COLOR_LIMIT); + if ((color_seq >= (0*kml_color_limit)) && (color_seq < (1*kml_color_limit))) { + kml_color_sequencer.color.bbggrr = (0)<<16 | (color_seq)<<8 | (kml_color_limit); + } else if ((color_seq >= (1*kml_color_limit)) && (color_seq < (2*kml_color_limit))) { + kml_color_sequencer.color.bbggrr = (0)<<16 | (kml_color_limit)<<8 | (2*kml_color_limit-color_seq); + } else if ((color_seq >= (2*kml_color_limit)) && (color_seq < (3*kml_color_limit))) { + kml_color_sequencer.color.bbggrr = (color_seq-2*kml_color_limit)<<16 | (kml_color_limit)<<8 | (0); + } else if ((color_seq >= (3*kml_color_limit)) && (color_seq < (4*kml_color_limit))) { + kml_color_sequencer.color.bbggrr = (kml_color_limit)<<16 | (4*kml_color_limit-color_seq)<<8 | (0); + } else if ((color_seq >= (4*kml_color_limit)) && (color_seq < (5*kml_color_limit))) { + kml_color_sequencer.color.bbggrr = (kml_color_limit)<<16 | (0)<<8 | (color_seq-4*kml_color_limit); + } else if ((color_seq >= (5*kml_color_limit)) && (color_seq < (6*kml_color_limit))) { + kml_color_sequencer.color.bbggrr = (6*kml_color_limit-color_seq)<<16 | (0)<<8 | (kml_color_limit); } else { // should not occur, but to be safe generate a legal color. warning(MYNAME ": Error in color conversion - using default color.\n"); kml_color_sequencer.color.bbggrr = (102)<<16 | (102)<<8 | (102); @@ -273,53 +115,14 @@ static void kml_step_color() kml_color_sequencer.seq = kml_color_sequencer.seq + kml_color_sequencer.step; } -static xg_callback wpt_s, wpt_e; -static xg_callback wpt_name, wpt_desc, wpt_coord, wpt_icon, trk_coord, wpt_time, wpt_ts_begin, wpt_ts_end; -static xg_callback gx_trk_s, gx_trk_e; -static xg_callback gx_trk_when, gx_trk_coord; - -static -xg_tag_mapping kml_map[] = { - { wpt_s, cb_start, "/Placemark" }, - { wpt_e, cb_end, "/Placemark" }, - { wpt_name, cb_cdata, "/Placemark/name" }, - { wpt_desc, cb_cdata, "/Placemark/description" }, - { wpt_ts_begin, cb_cdata, "/Placemark/TimeSpan/begin" }, - { wpt_ts_end, cb_cdata, "/Placemark/TimeSpan/end" }, - { wpt_time, cb_cdata, "/Placemark/TimeStamp/when" }, - // Alias for above used in KML 2.0 - { wpt_time, cb_cdata, "/Placemark/TimeInstant/timePosition" }, - { wpt_coord, cb_cdata, "/Placemark/Point/coordinates" }, - { wpt_icon, cb_cdata, "/Placemark/Style/Icon/href" }, - { trk_coord, cb_cdata, "/Placemark/MultiGeometry/LineString/coordinates" }, - { trk_coord, cb_cdata, "/Placemark/GeometryCollection/LineString/coordinates" }, - { trk_coord, cb_cdata, "/Placemark/Polygon/outerBoundaryIs/LinearRing/coordinates" }, - { trk_coord, cb_cdata, "/Placemark/LineString/coordinates" }, - { gx_trk_s, cb_start, "/Placemark/*gx:Track" }, - { gx_trk_e, cb_end, "/Placemark/*gx:Track" }, - { gx_trk_when, cb_cdata, "/Placemark/*gx:Track/when" }, - { gx_trk_coord, cb_cdata, "/Placemark/*gx:Track/gx:coord" }, - { gx_trk_s, cb_start, "/Placemark/Track" }, // KML 2.3 - { gx_trk_e, cb_end, "/Placemark/Track" }, // KML 2.3 - { gx_trk_when, cb_cdata, "/Placemark/Track/when" }, // KML 2.3 - { gx_trk_coord, cb_cdata, "/Placemark/Track/coord" }, // KML 2.3 - { gx_trk_s, cb_start, "/Placemark/MultiTrack/Track" }, // KML 2.3 - { gx_trk_e, cb_end, "/Placemark/MultiTrack/Track" }, // KML 2.3 - { gx_trk_when, cb_cdata, "/Placemark/MultiTrack/Track/when" }, // KML 2.3 - { gx_trk_coord, cb_cdata, "/Placemark/MultiTrack/Track/coord" }, // KML 2.3 - { nullptr, (xg_cb_type) 0, nullptr } -}; - -static -const char* kml_tags_to_ignore[] = { +const char* KmlFormat::kml_tags_to_ignore[] = { "kml", "Document", "Folder", nullptr }; -static -const char* kml_tags_to_skip[] = { +const char* KmlFormat::kml_tags_to_skip[] = { "Camera", "LookAt", "styleUrl", @@ -327,16 +130,13 @@ const char* kml_tags_to_skip[] = { nullptr }; -// The TimeSpan/begin and TimeSpan/end DateTimes: -static gpsbabel::DateTime wpt_timespan_begin, wpt_timespan_end; - -void wpt_s(xg_string, const QXmlStreamAttributes*) +void KmlFormat::wpt_s(xg_string /*args*/, const QXmlStreamAttributes* /*attrs*/) { if (wpt_tmp) { fatal(MYNAME ": wpt_s: invalid kml file\n"); } wpt_tmp = new Waypoint; - wpt_tmp_queued = 0; + wpt_tmp_queued = false; /* Invalidate timespan elements for a beginning Placemark, * so that each Placemark has its own (or no) TimeSpan. */ @@ -344,7 +144,7 @@ void wpt_s(xg_string, const QXmlStreamAttributes*) wpt_timespan_end = gpsbabel::DateTime(); } -void wpt_e(xg_string, const QXmlStreamAttributes*) +void KmlFormat::wpt_e(xg_string /*args*/, const QXmlStreamAttributes* /*attrs*/) { if (!wpt_tmp) { fatal(MYNAME ": wpt_e: invalid kml file\n"); @@ -356,10 +156,10 @@ void wpt_e(xg_string, const QXmlStreamAttributes*) delete wpt_tmp; wpt_tmp = nullptr; } - wpt_tmp_queued = 0; + wpt_tmp_queued = false; } -void wpt_name(xg_string args, const QXmlStreamAttributes*) +void KmlFormat::wpt_name(xg_string args, const QXmlStreamAttributes* /*attrs*/) { if (!wpt_tmp) { fatal(MYNAME ": wpt_name: invalid kml file\n"); @@ -367,7 +167,7 @@ void wpt_name(xg_string args, const QXmlStreamAttributes*) wpt_tmp->shortname = args; } -void wpt_desc(const QString& args, const QXmlStreamAttributes*) +void KmlFormat::wpt_desc(const QString& args, const QXmlStreamAttributes* /*attrs*/) { if (!wpt_tmp) { fatal(MYNAME ": wpt_desc: invalid kml file\n"); @@ -375,7 +175,7 @@ void wpt_desc(const QString& args, const QXmlStreamAttributes*) wpt_tmp->description += args.trimmed(); } -void wpt_time(xg_string args, const QXmlStreamAttributes*) +void KmlFormat::wpt_time(xg_string args, const QXmlStreamAttributes* /*attrs*/) { if (!wpt_tmp) { fatal(MYNAME ": wpt_time: invalid kml file\n"); @@ -383,17 +183,17 @@ void wpt_time(xg_string args, const QXmlStreamAttributes*) wpt_tmp->SetCreationTime(xml_parse_time(args)); } -void wpt_ts_begin(xg_string args, const QXmlStreamAttributes*) +void KmlFormat::wpt_ts_begin(xg_string args, const QXmlStreamAttributes* /*attrs*/) { wpt_timespan_begin = xml_parse_time(args); } -void wpt_ts_end(xg_string args, const QXmlStreamAttributes*) +void KmlFormat::wpt_ts_end(xg_string args, const QXmlStreamAttributes* /*attrs*/) { wpt_timespan_end = xml_parse_time(args); } -void wpt_coord(const QString& args, const QXmlStreamAttributes*) +void KmlFormat::wpt_coord(const QString& args, const QXmlStreamAttributes* /*attrs*/) { double lat, lon, alt; if (! wpt_tmp) { @@ -408,17 +208,17 @@ void wpt_coord(const QString& args, const QXmlStreamAttributes*) if (n == 3) { wpt_tmp->altitude = alt; } - wpt_tmp_queued = 1; + wpt_tmp_queued = true; } -void wpt_icon(xg_string args, const QXmlStreamAttributes*) +void KmlFormat::wpt_icon(xg_string args, const QXmlStreamAttributes* /*attrs*/) { if (wpt_tmp) { wpt_tmp->icon_descr = args; } } -void trk_coord(xg_string args, const QXmlStreamAttributes*) +void KmlFormat::trk_coord(xg_string args, const QXmlStreamAttributes* /*attrs*/) { auto* trk_head = new route_head; if (wpt_tmp && !wpt_tmp->shortname.isEmpty()) { @@ -427,10 +227,10 @@ void trk_coord(xg_string args, const QXmlStreamAttributes*) track_add_head(trk_head); const auto vecs = args.simplified().split(' '); - for(const auto& vec : vecs) { + for (const auto& vec : vecs) { const QStringList coords = vec.split(','); auto csize = coords.size(); - auto trkpt = new Waypoint; + auto* trkpt = new Waypoint; if (csize == 3) { trkpt->altitude = coords[2].toDouble(); @@ -470,7 +270,7 @@ void trk_coord(xg_string args, const QXmlStreamAttributes*) } } -void gx_trk_s(xg_string, const QXmlStreamAttributes*) +void KmlFormat::gx_trk_s(xg_string /*args*/, const QXmlStreamAttributes* /*attrs*/) { gx_trk_head = new route_head; if (wpt_tmp && !wpt_tmp->shortname.isEmpty()) { @@ -486,7 +286,7 @@ void gx_trk_s(xg_string, const QXmlStreamAttributes*) gx_trk_coords = new QList>; } -void gx_trk_e(xg_string, const QXmlStreamAttributes*) +void KmlFormat::gx_trk_e(xg_string /*args*/, const QXmlStreamAttributes* /*attrs*/) { // Check that for every temporal value (kml:when) in a kml:Track there is a position (kml:coord) value. // Check that for every temporal value (kml:when) in a gx:Track there is a position (gx:coord) value. @@ -529,7 +329,7 @@ void gx_trk_e(xg_string, const QXmlStreamAttributes*) gx_trk_coords = nullptr; } -void gx_trk_when(xg_string args, const QXmlStreamAttributes*) +void KmlFormat::gx_trk_when(xg_string args, const QXmlStreamAttributes* /*attrs*/) { if (! gx_trk_times) { fatal(MYNAME ": gx_trk_when: invalid kml file\n"); @@ -537,7 +337,7 @@ void gx_trk_when(xg_string args, const QXmlStreamAttributes*) gx_trk_times->append(xml_parse_time(args)); } -void gx_trk_coord(xg_string args, const QXmlStreamAttributes*) +void KmlFormat::gx_trk_coord(xg_string args, const QXmlStreamAttributes* /*attrs*/) { if (! gx_trk_coords) { fatal(MYNAME ": gx_trk_coord: invalid kml file\n"); @@ -551,28 +351,22 @@ void gx_trk_coord(xg_string args, const QXmlStreamAttributes*) gx_trk_coords->append(std::make_tuple(n, lat, lon, alt)); } -static -void -kml_rd_init(const QString& fname) +void KmlFormat::rd_init(const QString& fname) { - xml_init(fname, kml_map, nullptr, kml_tags_to_ignore, kml_tags_to_skip); + xml_init(fname, build_xg_tag_map(this, kml_map), nullptr, kml_tags_to_ignore, kml_tags_to_skip, true); } -static -void -kml_read() +void KmlFormat::read() { xml_read(); } -static void -kml_rd_deinit() +void KmlFormat::rd_deinit() { xml_deinit(); } -static void -kml_wr_init(const QString& fname) +void KmlFormat::wr_init(const QString& fname) { char u = 's'; waypt_init_bounds(&kml_bounds); @@ -614,8 +408,7 @@ kml_wr_init(const QString& fname) * The magic here is to try to ensure that posnfilename is atomically * updated. */ -static void -kml_wr_position_init(const QString& fname) +void KmlFormat::wr_position_init(const QString& fname) { posnfilename = fname; posnfilenametmp = QString("%1-").arg(fname); @@ -623,8 +416,7 @@ kml_wr_position_init(const QString& fname) max_position_points = atoi(opt_max_position_points); } -static void -kml_wr_deinit() +void KmlFormat::wr_deinit() { writer->writeEndDocument(); delete writer; @@ -642,8 +434,7 @@ kml_wr_deinit() } } -static void -kml_wr_position_deinit() +void KmlFormat::wr_position_deinit() { // kml_wr_deinit(); posnfilename.clear(); @@ -651,8 +442,7 @@ kml_wr_position_deinit() } -static void -kml_output_linestyle(char* /*color*/, int width) +void KmlFormat::kml_output_linestyle(char* /*color*/, int width) const { // Style settings for line strings writer->writeStartElement(QStringLiteral("LineStyle")); @@ -662,8 +452,8 @@ kml_output_linestyle(char* /*color*/, int width) } -static void kml_write_bitmap_style_(const QString& style, const QString& bitmap, - int highlighted, int force_heading) +void KmlFormat::kml_write_bitmap_style_(const QString& style, const QString& bitmap, + int highlighted, int force_heading) const { int is_track = style.startsWith("track"); int is_multitrack = style.startsWith("multiTrack"); @@ -708,8 +498,8 @@ static void kml_write_bitmap_style_(const QString& style, const QString& bitmap, * and non-highlighted version of the style to allow the icons * to magnify slightly on a rollover. */ -static void kml_write_bitmap_style(kml_point_type pt_type, const QString& bitmap, - const QString& customstyle) +void KmlFormat::kml_write_bitmap_style(kml_point_type pt_type, const QString& bitmap, + const QString& customstyle) const { int force_heading = 0; QString style; @@ -751,7 +541,7 @@ static void kml_write_bitmap_style(kml_point_type pt_type, const QString& bitmap writer->writeEndElement(); // Close StyleMap tag } -static void kml_output_timestamp(const Waypoint* waypointp) +void KmlFormat::kml_output_timestamp(const Waypoint* waypointp) const { QString time_string = waypointp->CreationTimeXML(); if (!time_string.isEmpty()) { @@ -761,8 +551,7 @@ static void kml_output_timestamp(const Waypoint* waypointp) } } -static -void kml_td(gpsbabel::XmlStreamWriter& hwriter, const QString& boldData, const QString& data) +void KmlFormat::kml_td(gpsbabel::XmlStreamWriter& hwriter, const QString& boldData, const QString& data) { hwriter.writeCharacters(QStringLiteral("\n")); hwriter.writeStartElement(QStringLiteral("tr")); @@ -773,8 +562,7 @@ void kml_td(gpsbabel::XmlStreamWriter& hwriter, const QString& boldData, const Q hwriter.writeEndElement(); // Close tr tag } -static -void kml_td(gpsbabel::XmlStreamWriter& hwriter, const QString& data) +void KmlFormat::kml_td(gpsbabel::XmlStreamWriter& hwriter, const QString& data) { hwriter.writeCharacters(QStringLiteral("\n")); hwriter.writeStartElement(QStringLiteral("tr")); @@ -787,8 +575,7 @@ void kml_td(gpsbabel::XmlStreamWriter& hwriter, const QString& data) /* * Output the track summary. */ -static -void kml_output_trkdescription(const route_head* header, const computed_trkdata* td) +void KmlFormat::kml_output_trkdescription(const route_head* header, const computed_trkdata* td) const { if (!td || !trackdata) { return; @@ -874,12 +661,9 @@ void kml_output_trkdescription(const route_head* header, const computed_trkdata* } -static -void kml_output_header(const route_head* header, const computed_trkdata* td) +void KmlFormat::kml_output_header(const route_head* header, const computed_trkdata* td) const { - if (!realtime_positioning) { - writer->writeStartElement(QStringLiteral("Folder")); - } + writer->writeStartElement(QStringLiteral("Folder")); writer->writeOptionalTextElement(QStringLiteral("name"), header->rte_name); kml_output_trkdescription(header, td); @@ -890,8 +674,7 @@ void kml_output_header(const route_head* header, const computed_trkdata* td) } } -static -int kml_altitude_known(const Waypoint* waypoint) +int KmlFormat::kml_altitude_known(const Waypoint* waypoint) const { if (waypoint->altitude == unknown_alt) { return 0; @@ -904,8 +687,7 @@ int kml_altitude_known(const Waypoint* waypoint) return 1; } -static -void kml_write_coordinates(const Waypoint* waypointp) +void KmlFormat::kml_write_coordinates(const Waypoint* waypointp) const { if (kml_altitude_known(waypointp)) { writer->writeTextElement(QStringLiteral("coordinates"), @@ -924,7 +706,7 @@ void kml_write_coordinates(const Waypoint* waypointp) /* Rather than a default "top down" view, view from the side to highlight * topo features. */ -static void kml_output_lookat(const Waypoint* waypointp) +void KmlFormat::kml_output_lookat(const Waypoint* waypointp) const { writer->writeStartElement(QStringLiteral("LookAt")); writer->writeTextElement(QStringLiteral("longitude"), QString::number(waypointp->longitude, 'f', precision)); @@ -933,7 +715,7 @@ static void kml_output_lookat(const Waypoint* waypointp) writer->writeEndElement(); // Close LookAt tag } -static void kml_output_positioning(bool tessellate) +void KmlFormat::kml_output_positioning(bool tessellate) const { // These elements must be output as a sequence, i.e. in order. if (extrude) { @@ -951,7 +733,7 @@ static void kml_output_positioning(bool tessellate) } /* Output something interesting when we can for route and trackpoints */ -static void kml_output_description(const Waypoint* pt) +void KmlFormat::kml_output_description(const Waypoint* pt) const { const char* alt_units; @@ -1021,7 +803,7 @@ static void kml_output_description(const Waypoint* pt) writer->writeEndElement(); // Close description tag } -static void kml_recompute_time_bounds(const Waypoint* waypointp) +void KmlFormat::kml_recompute_time_bounds(const Waypoint* waypointp) { if (waypointp->GetCreationTime().isValid()) { if (!(kml_time_min.isValid()) || @@ -1035,13 +817,13 @@ static void kml_recompute_time_bounds(const Waypoint* waypointp) } } -static void kml_add_to_bounds(const Waypoint* waypointp) +void KmlFormat::kml_add_to_bounds(const Waypoint* waypointp) { waypt_add_to_bounds(&kml_bounds, waypointp); kml_recompute_time_bounds(waypointp); } -static void kml_output_point(const Waypoint* waypointp, kml_point_type pt_type) +void KmlFormat::kml_output_point(const Waypoint* waypointp, kml_point_type pt_type) const { QString style; @@ -1100,7 +882,7 @@ static void kml_output_point(const Waypoint* waypointp, kml_point_type pt_type) } } -static void kml_output_tailer(const route_head* header) +void KmlFormat::kml_output_tailer(const route_head* header) { if (export_points && header->rte_waypt_ct > 0) { @@ -1178,9 +960,7 @@ static void kml_output_tailer(const route_head* header) writer->writeEndElement(); // Close Placemark tag } - if (!realtime_positioning) { - writer->writeEndElement(); // Close folder tag - } + writer->writeEndElement(); // Close folder tag } /* @@ -1188,8 +968,7 @@ static void kml_output_tailer(const route_head* header) */ // Text that's common to all tabs. -static -void kml_gc_all_tabs_text(QString& cdataStr) +void KmlFormat::kml_gc_all_tabs_text(QString& cdataStr) { // cdataStr.append(" \n"); cdataStr.append("\n"); @@ -1201,21 +980,19 @@ void kml_gc_all_tabs_text(QString& cdataStr) } -static const QString map_templates[] = { - "Google Maps", - "Geocaching.com Google Map", - "MyTopo Maps", - "MapQuest", - "Bing Maps", - "Rand McNally", - "Open Cycle Maps", - "Open Street Maps", +const QString KmlFormat::map_templates[] = { + R"(Google Maps)", + R"(Geocaching.com Google Map)", + R"(MyTopo Maps)", + R"(MapQuest)", + R"(Bing Maps)", + R"(Rand McNally)", + R"(Open Cycle Maps)", + R"(Open Street Maps)", nullptr }; - -static -void kml_gc_make_balloonstyletext() +void KmlFormat::kml_gc_make_balloonstyletext() const { QString cdataStr; @@ -1267,7 +1044,7 @@ void kml_gc_make_balloonstyletext() cdataStr.append("
    \n"); // Fortunately, all the mappy map URLs take lat/longs in the URLs, so // the substitution is easy. - for (int tp = 0; !map_templates[tp].isEmpty(); tp++) { + for (int tp = 0; !map_templates[tp].isEmpty(); ++tp) { cdataStr.append("
  • \n"); cdataStr.append(" "); cdataStr.append(map_templates[tp]); @@ -1286,8 +1063,7 @@ void kml_gc_make_balloonstyletext() writer->writeEndElement(); // Close BalloonStyle tag } -static -void kml_gc_make_balloonstyle() +void KmlFormat::kml_gc_make_balloonstyle() const { // For Normal style of gecoaches, scale of label is set to zero // to make the label invisible. On hover (highlight?) enlarge @@ -1334,9 +1110,7 @@ void kml_gc_make_balloonstyle() writer->writeEndElement(); // Close StyleMap tag } -static -QString -kml_lookup_gc_icon(const Waypoint* waypointp) +QString KmlFormat::kml_lookup_gc_icon(const Waypoint* waypointp) { const char* icon; /* This could be done so much better in C99 with designated @@ -1390,9 +1164,7 @@ kml_lookup_gc_icon(const Waypoint* waypointp) return QString("https://www.geocaching.com/images/kml/%1").arg(icon); } -static const -char* -kml_lookup_gc_container(const Waypoint* waypointp) +const char* KmlFormat::kml_lookup_gc_container(const Waypoint* waypointp) { const char* cont; @@ -1423,7 +1195,7 @@ kml_lookup_gc_container(const Waypoint* waypointp) return cont; } -static QString kml_gc_mkstar(int rating) +QString KmlFormat::kml_gc_mkstar(int rating) { QString star_content; @@ -1441,7 +1213,7 @@ static QString kml_gc_mkstar(int rating) } -static QString kml_geocache_get_logs(const Waypoint* wpt) +QString KmlFormat::kml_geocache_get_logs(const Waypoint* wpt) const { QString r; @@ -1498,7 +1270,7 @@ static QString kml_geocache_get_logs(const Waypoint* wpt) return r; } -static void kml_write_data_element(const QString& name, const QString& value) +void KmlFormat::kml_write_data_element(const QString& name, const QString& value) const { writer->writeStartElement(QStringLiteral("Data")); writer->writeAttribute(QStringLiteral("name"), name); @@ -1506,7 +1278,7 @@ static void kml_write_data_element(const QString& name, const QString& value) writer->writeEndElement(); // Close Data tag } -static void kml_write_data_element(const QString& name, const int value) +void KmlFormat::kml_write_data_element(const QString& name, const int value) const { writer->writeStartElement(QStringLiteral("Data")); writer->writeAttribute(QStringLiteral("name"), name); @@ -1514,7 +1286,7 @@ static void kml_write_data_element(const QString& name, const int value) writer->writeEndElement(); // Close Data tag } -static void kml_write_data_element(const QString& name, const double value) +void KmlFormat::kml_write_data_element(const QString& name, const double value) const { writer->writeStartElement(QStringLiteral("Data")); writer->writeAttribute(QStringLiteral("name"), name); @@ -1522,7 +1294,7 @@ static void kml_write_data_element(const QString& name, const double value) writer->writeEndElement(); // Close Data tag } -static void kml_write_cdata_element(const QString& name, const QString& value) +void KmlFormat::kml_write_cdata_element(const QString& name, const QString& value) const { writer->writeStartElement(QStringLiteral("Data")); writer->writeAttribute(QStringLiteral("name"), name); @@ -1532,7 +1304,7 @@ static void kml_write_cdata_element(const QString& name, const QString& value) writer->writeEndElement(); // Close Data tag } -static void kml_geocache_pr(const Waypoint* waypointp) +void KmlFormat::kml_geocache_pr(const Waypoint* waypointp) const { const char* issues = ""; @@ -1617,7 +1389,7 @@ static void kml_geocache_pr(const Waypoint* waypointp) * WAYPOINTS */ -static void kml_waypt_pr(const Waypoint* waypointp) +void KmlFormat::kml_waypt_pr(const Waypoint* waypointp) const { QString icon; @@ -1689,7 +1461,7 @@ static void kml_waypt_pr(const Waypoint* waypointp) * TRACKPOINTS */ -static void kml_track_hdr(const route_head* header) +void KmlFormat::kml_track_hdr(const route_head* header) const { computed_trkdata td = track_recompute(header); if (header->rte_waypt_ct > 0 && (export_lines || export_points)) { @@ -1697,12 +1469,12 @@ static void kml_track_hdr(const route_head* header) } } -static void kml_track_disp(const Waypoint* waypointp) +void KmlFormat::kml_track_disp(const Waypoint* waypointp) const { kml_output_point(waypointp, kmlpt_track); } -static void kml_track_tlr(const route_head* header) +void KmlFormat::kml_track_tlr(const route_head* header) { if (header->rte_waypt_ct > 0 && (export_lines || export_points)) { kml_output_tailer(header); @@ -1715,19 +1487,9 @@ static void kml_track_tlr(const route_head* header) * callback as we have to make multiple passes over the track queues. */ -// Helper to write gx:SimpleList, iterating over a route queue and writing out. - -enum wp_field { - fld_cadence, - fld_depth, - fld_heartrate, - fld_temperature, - fld_power -}; - -static void kml_mt_simple_array(const route_head* header, - const char* name, - wp_field member) +void KmlFormat::kml_mt_simple_array(const route_head* header, + const char* name, + wp_field member) const { writer->writeStartElement(QStringLiteral("gx:SimpleArrayData")); writer->writeAttribute(QStringLiteral("name"), name); @@ -1758,7 +1520,7 @@ static void kml_mt_simple_array(const route_head* header, } // True if at least two points in the track have timestamps. -static int track_has_time(const route_head* header) +int KmlFormat::track_has_time(const route_head* header) { int points_with_time = 0; foreach (const Waypoint* tpt, header->waypoint_list) { @@ -1773,7 +1535,7 @@ static int track_has_time(const route_head* header) } // Simulate a track_disp_all callback sequence for a single track. -static void write_as_linestring(const route_head* header) +void KmlFormat::write_as_linestring(const route_head* header) { kml_track_hdr(header); foreach (const Waypoint* tpt, header->waypoint_list) { @@ -1783,7 +1545,7 @@ static void write_as_linestring(const route_head* header) } -static void kml_mt_hdr(const route_head* header) +void KmlFormat::kml_mt_hdr(const route_head* header) { int has_cadence = 0; int has_depth = 0; @@ -1879,7 +1641,7 @@ static void kml_mt_hdr(const route_head* header) } } -static void kml_mt_tlr(const route_head* header) +void KmlFormat::kml_mt_tlr(const route_head* header) const { if (track_has_time(header)) { writer->writeEndElement(); // Close gx:Track tag @@ -1891,17 +1653,17 @@ static void kml_mt_tlr(const route_head* header) * ROUTES */ -static void kml_route_hdr(const route_head* header) +void KmlFormat::kml_route_hdr(const route_head* header) const { kml_output_header(header, nullptr); } -static void kml_route_disp(const Waypoint* waypointp) +void KmlFormat::kml_route_disp(const Waypoint* waypointp) const { kml_output_point(waypointp, kmlpt_route); } -static void kml_route_tlr(const route_head* header) +void KmlFormat::kml_route_tlr(const route_head* header) { kml_output_tailer(header); } @@ -1909,17 +1671,20 @@ static void kml_route_tlr(const route_head* header) // For Earth 5.0 and later, we write a LookAt that encompasses // the bounding box of our entire data set and set the event times // to include all our data. -static void kml_write_AbstractView() +void KmlFormat::kml_write_AbstractView() { // Make a pass through all the points to find the bounds. + auto kml_add_to_bounds_lambda = [this](const Waypoint* waypointp)->void { + kml_add_to_bounds(waypointp); + }; if (waypt_count()) { - waypt_disp_all(kml_add_to_bounds); + waypt_disp_all(kml_add_to_bounds_lambda); } if (track_waypt_count()) { - track_disp_all(nullptr, nullptr, kml_add_to_bounds); + track_disp_all(nullptr, nullptr, kml_add_to_bounds_lambda); } if (route_waypt_count()) { - route_disp_all(nullptr, nullptr, kml_add_to_bounds); + route_disp_all(nullptr, nullptr, kml_add_to_bounds_lambda); } writer->writeStartElement(QStringLiteral("LookAt")); @@ -1937,9 +1702,8 @@ static void kml_write_AbstractView() // the network position. So we shove the end of the timespan out to // ensure the right edge of that time slider includes us. // - gpsbabel::DateTime time_max; - time_max = realtime_positioning ? kml_time_max.addSecs(600) - : kml_time_max; + gpsbabel::DateTime time_max = realtime_positioning ? kml_time_max.addSecs(600) + : kml_time_max; writer->writeTextElement(QStringLiteral("end"), time_max.toPrettyString()); } writer->writeEndElement(); // Close gx:TimeSpan tag @@ -1968,9 +1732,8 @@ static void kml_write_AbstractView() writer->writeEndElement(); // Close LookAt tag } -static -void kml_mt_array_schema(const char* field_name, const char* display_name, - const char* type) +void KmlFormat::kml_mt_array_schema(const char* field_name, const char* display_name, + const char* type) const { writer->writeStartElement(QStringLiteral("gx:SimpleArrayField")); writer->writeAttribute(QStringLiteral("name"), field_name); @@ -1979,7 +1742,7 @@ void kml_mt_array_schema(const char* field_name, const char* display_name, writer->writeEndElement(); // Close gx:SimpleArrayField tag } -static void kml_write() +void KmlFormat::write() { const global_trait* traits = get_traits(); @@ -2031,7 +1794,7 @@ static void kml_write() if (track_waypt_count()) { if (trackdirection) { kml_write_bitmap_style(kmlpt_other, ICON_TRK, "track-none"); - for (int i = 0; i < 16; i++) { + for (int i = 0; i < 16; ++i) { kml_write_bitmap_style(kmlpt_other, QString(ICON_DIR).arg(i), QString("track-%1").arg(i)); } } else { @@ -2087,7 +1850,10 @@ static void kml_write() writer->writeTextElement(QStringLiteral("name"), QStringLiteral("Waypoints")); } - waypt_disp_all(kml_waypt_pr); + auto kml_waypt_pr_lambda = [this](const Waypoint* waypointp)->void { + kml_waypt_pr(waypointp); + }; + waypt_disp_all(kml_waypt_pr_lambda); if (!realtime_positioning) { writer->writeEndElement(); // Close Folder tag @@ -2103,11 +1869,26 @@ static void kml_write() kml_init_color_sequencer(track_count()); if (export_track) { - track_disp_all(kml_mt_hdr, kml_mt_tlr, nullptr); + auto kml_mt_hdr_lambda = [this](const route_head* rte)->void { + kml_mt_hdr(rte); + }; + auto kml_mt_tlr_lambda = [this](const route_head* rte)->void { + kml_mt_tlr(rte); + }; + track_disp_all(kml_mt_hdr_lambda, kml_mt_tlr_lambda, nullptr); } - track_disp_all(kml_track_hdr, kml_track_tlr, - kml_track_disp); + auto kml_track_hdr_lambda = [this](const route_head* rte)->void { + kml_track_hdr(rte); + }; + auto kml_track_tlr_lambda = [this](const route_head* rte)->void { + kml_track_tlr(rte); + }; + auto kml_track_disp_lambda = [this](const Waypoint* waypointp)->void { + kml_track_disp(waypointp); + }; + track_disp_all(kml_track_hdr_lambda, kml_track_tlr_lambda, + kml_track_disp_lambda); if (!realtime_positioning) { writer->writeEndElement(); // Close Folder tag @@ -2121,8 +1902,17 @@ static void kml_write() writer->writeTextElement(QStringLiteral("name"), QStringLiteral("Routes")); kml_init_color_sequencer(route_count()); - route_disp_all(kml_route_hdr, - kml_route_tlr, kml_route_disp); + auto kml_route_hdr_lambda = [this](const route_head* rte)->void { + kml_route_hdr(rte); + }; + auto kml_route_tlr_lambda = [this](const route_head* rte)->void { + kml_route_tlr(rte); + }; + auto kml_route_disp_lambda = [this](const Waypoint* waypointp)->void { + kml_route_disp(waypointp); + }; + route_disp_all(kml_route_hdr_lambda, + kml_route_tlr_lambda, kml_route_disp_lambda); writer->writeEndElement(); // Close Folder tag } } @@ -2134,32 +1924,35 @@ static void kml_write() /* * This depends on the table being sorted correctly. */ -static const -char* -kml_get_posn_icon(int freshness) -{ - int n_stations = sizeof(kml_tracking_icons) / sizeof(kml_tracking_icons[0]); - - for (int i = 0; i < n_stations ; i++) { - if (freshness >= kml_tracking_icons[i].freshness) { - return kml_tracking_icons[i].icon; +QString KmlFormat::kml_get_posn_icon(int freshness) +{ + struct kml_tracking_icon { + int freshness; + QString icon; + }; + static const QVector kml_tracking_icons = { + { 60, ICON_BASE "youarehere-60.png" }, // Red + { 30, ICON_BASE "youarehere-30.png" }, // Yellow + { 0, ICON_BASE "youarehere-0.png" }, // Green + }; + + for (const auto& entry : kml_tracking_icons) { + if (freshness >= entry.freshness) { + return entry.icon; } } return ICON_NOSAT; } - -static route_head* posn_trk_head = nullptr; - -static void -kml_wr_position(Waypoint* wpt) +void KmlFormat::wr_position(Waypoint* wpt) { static gpsbabel::DateTime last_valid_fix; - kml_wr_init(posnfilenametmp); + wr_init(posnfilenametmp); if (!posn_trk_head) { posn_trk_head = new route_head; + posn_trk_head->rte_name = "Track"; track_add_head(posn_trk_head); } @@ -2192,7 +1985,9 @@ kml_wr_position(Waypoint* wpt) /* In order to avoid clutter while we're sitting still, don't add track points if we've not moved a minimum distance from the beginning of our accumulated track. */ - { + if (posn_trk_head->waypoint_list.empty()) { + track_add_wpt(posn_trk_head, new Waypoint(*wpt)); + } else { Waypoint* newest_posn= posn_trk_head->waypoint_list.back(); if (radtometers(gcdist(RAD(wpt->latitude), RAD(wpt->longitude), @@ -2209,7 +2004,7 @@ kml_wr_position(Waypoint* wpt) } waypt_add(wpt); - kml_write(); + write(); waypt_del(wpt); /* @@ -2222,21 +2017,5 @@ kml_wr_position(Waypoint* wpt) track_del_wpt(posn_trk_head, tonuke); } - kml_wr_deinit(); + wr_deinit(); } - -ff_vecs_t kml_vecs = { - ff_type_file, - FF_CAP_RW_ALL, /* Format can do RW_ALL */ - kml_rd_init, - kml_wr_init, - kml_rd_deinit, - kml_wr_deinit, - kml_read, - kml_write, - nullptr, - &kml_args, - CET_CHARSET_UTF8, 1, /* CET-REVIEW */ - { nullptr, nullptr, nullptr, kml_wr_position_init, kml_wr_position, kml_wr_position_deinit }, - nullptr -}; diff --git a/kml.h b/kml.h new file mode 100644 index 000000000..edf2a4e6b --- /dev/null +++ b/kml.h @@ -0,0 +1,348 @@ +/* + Support for Google Earth & Keyhole "kml" format. + + Copyright (C) 2005-2013 Robert Lipe, robertlipe+source@gpsbabel.org + Updates by Andrew Kirmse, akirmse at google.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + */ +#ifndef KML_H_INCLUDED_ +#define KML_H_INCLUDED_ + +#ifdef __WIN32__ +# include +#endif + +#include // for tuple, make_tuple, tie + +#include // for QList +#include // for QString, QStringLiteral, operator+, operator!= +#include // for QVector +#include // for QXmlStreamAttributes + +#include "defs.h" +#include "format.h" +#include "src/core/datetime.h" // for DateTime +#include "src/core/file.h" // for File +#include "src/core/xmlstreamwriter.h" // for XmlStreamWriter +#include "xmlgeneric.h" // for cb_cdata, cb_end, cb_start, xg_callback, xg_string, xg_cb_type, xml_deinit, xml_ignore_tags, xml_init, xml_read, xg_tag_mapping + + +class KmlFormat : public Format +{ +public: + QVector* get_args() override + { + return &kml_args; + } + + ff_type get_type() const override + { + return ff_type_file; + } + + QVector get_cap() const override + { + return FF_CAP_RW_ALL; + } + + QString get_encode() const override + { + return CET_CHARSET_UTF8; + } + + int get_fixed_encode() const override + { + return 1; + } + + void rd_init(const QString& fname) override; + void read() override; + void rd_deinit() override; + void wr_init(const QString& fname) override; + void write() override; + void wr_deinit() override; + void wr_position_init(const QString& fname) override; + void wr_position(Waypoint* wpt) override; + void wr_position_deinit() override; + +private: + /* Types */ + + enum kml_point_type { + kmlpt_unknown, + kmlpt_waypoint, + kmlpt_track, + kmlpt_route, + kmlpt_multitrack, + kmlpt_other + }; + +// Helper to write gx:SimpleList, iterating over a route queue and writing out. + + enum wp_field { + fld_cadence, + fld_depth, + fld_heartrate, + fld_temperature, + fld_power + }; + + /* Constants */ + static constexpr const char* default_precision = "6"; + static constexpr int kml_color_limit = 204; /* allowed range [0,255] */ + + // Multitrack ids to correlate Schema to SchemaData + static constexpr const char* kmt_heartrate = "heartrate"; + static constexpr const char* kmt_cadence = "cadence"; + static constexpr const char* kmt_temperature = "temperature"; + static constexpr const char* kmt_depth = "depth"; + static constexpr const char* kmt_power = "power"; + + /* Member Functions */ + + void kml_init_color_sequencer(unsigned int steps_per_rev); + void kml_step_color(); + void wpt_s(const QString& args, const QXmlStreamAttributes* attrs); + void wpt_e(const QString& args, const QXmlStreamAttributes* attrs); + void wpt_name(const QString& args, const QXmlStreamAttributes* attrs); + void wpt_desc(const QString& args, const QXmlStreamAttributes* attrs); + void wpt_coord(const QString& args, const QXmlStreamAttributes* attrs); + void wpt_icon(const QString& args, const QXmlStreamAttributes* attrs); + void trk_coord(const QString& args, const QXmlStreamAttributes* attrs); + void wpt_time(const QString& args, const QXmlStreamAttributes* attrs); + void wpt_ts_begin(const QString& args, const QXmlStreamAttributes* attrs); + void wpt_ts_end(const QString& args, const QXmlStreamAttributes* attrs); + void gx_trk_s(const QString& args, const QXmlStreamAttributes* attrs); + void gx_trk_e(const QString& args, const QXmlStreamAttributes* attrs); + void gx_trk_when(const QString& args, const QXmlStreamAttributes* attrs); + void gx_trk_coord(const QString& args, const QXmlStreamAttributes* attrs); + void kml_output_linestyle(char* color, int width) const; + void kml_write_bitmap_style_(const QString& style, const QString& bitmap, int highlighted, int force_heading) const; + void kml_write_bitmap_style(kml_point_type pt_type, const QString& bitmap, const QString& customstyle) const; + void kml_output_timestamp(const Waypoint* waypointp) const; + static void kml_td(gpsbabel::XmlStreamWriter& hwriter, const QString& boldData, const QString& data); + static void kml_td(gpsbabel::XmlStreamWriter& hwriter, const QString& data); + void kml_output_trkdescription(const route_head* header, const computed_trkdata* td) const; + void kml_output_header(const route_head* header, const computed_trkdata* td) const; + int kml_altitude_known(const Waypoint* waypoint) const; + void kml_write_coordinates(const Waypoint* waypointp) const; + void kml_output_lookat(const Waypoint* waypointp) const; + void kml_output_positioning(bool tessellate) const; + void kml_output_description(const Waypoint* pt) const; + void kml_recompute_time_bounds(const Waypoint* waypointp); + void kml_add_to_bounds(const Waypoint* waypointp); + void kml_output_point(const Waypoint* waypointp, kml_point_type pt_type) const; + void kml_output_tailer(const route_head* header); + static void kml_gc_all_tabs_text(QString& cdataStr); + void kml_gc_make_balloonstyletext() const; + void kml_gc_make_balloonstyle() const; + static QString kml_lookup_gc_icon(const Waypoint* waypointp); + static const char* kml_lookup_gc_container(const Waypoint* waypointp); + static QString kml_gc_mkstar(int rating); + QString kml_geocache_get_logs(const Waypoint* wpt) const; + void kml_write_data_element(const QString& name, const QString& value) const; + void kml_write_data_element(const QString& name, int value) const; + void kml_write_data_element(const QString& name, double value) const; + void kml_write_cdata_element(const QString& name, const QString& value) const; + void kml_geocache_pr(const Waypoint* waypointp) const; + void kml_waypt_pr(const Waypoint* waypointp) const; + void kml_track_hdr(const route_head* header) const; + void kml_track_disp(const Waypoint* waypointp) const; + void kml_track_tlr(const route_head* header); + void kml_mt_simple_array(const route_head* header, const char* name, wp_field member) const; + static int track_has_time(const route_head* header); + void write_as_linestring(const route_head* header); + void kml_mt_hdr(const route_head* header); + void kml_mt_tlr(const route_head* header) const; + void kml_route_hdr(const route_head* header) const; + void kml_route_disp(const Waypoint* waypointp) const; + void kml_route_tlr(const route_head* header); + void kml_write_AbstractView(); + void kml_mt_array_schema(const char* field_name, const char* display_name, const char* type) const; + static QString kml_get_posn_icon(int freshness); + + /* Data Members */ + + // options + char* opt_deficon{nullptr}; + char* opt_export_lines{nullptr}; + char* opt_export_points{nullptr}; + char* opt_export_track{nullptr}; + char* opt_line_width{nullptr}; + char* opt_line_color{nullptr}; + char* opt_floating{nullptr}; + char* opt_extrude{nullptr}; + char* opt_trackdata{nullptr}; + char* opt_trackdirection{nullptr}; + char* opt_units{nullptr}; + char* opt_labels{nullptr}; + char* opt_max_position_points{nullptr}; + char* opt_rotate_colors{nullptr}; + char* opt_precision{nullptr}; + + int export_lines{}; + int export_points{}; + int export_track{}; + int floating{}; + int extrude{}; + int trackdata{}; + int trackdirection{}; + int max_position_points{}; + int rotate_colors{}; + int line_width{}; + int html_encrypt{}; + int precision{}; + + Waypoint* wpt_tmp{nullptr}; + bool wpt_tmp_queued{false}; + QString posnfilename; + QString posnfilenametmp; + + route_head* gx_trk_head{nullptr}; + QList* gx_trk_times{nullptr}; + QList>* gx_trk_coords{nullptr}; + + gpsbabel::File* oqfile{nullptr}; + gpsbabel::XmlStreamWriter* writer{nullptr}; + + int realtime_positioning{}; + bounds kml_bounds{}; + gpsbabel::DateTime kml_time_max; + gpsbabel::DateTime kml_time_min; + + QVector kml_args = { + {"deficon", &opt_deficon, "Default icon name", nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr }, + { + "lines", &opt_export_lines, + "Export linestrings for tracks and routes", + "1", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr, + }, + { + "points", &opt_export_points, + "Export placemarks for tracks and routes", + "1", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr + }, + { + "line_width", &opt_line_width, + "Width of lines, in pixels", + "6", ARGTYPE_INT, ARG_NOMINMAX, nullptr + }, + { + "line_color", &opt_line_color, + "Line color, specified in hex AABBGGRR", + "99ffac59", ARGTYPE_STRING, ARG_NOMINMAX, nullptr + }, + { + "floating", &opt_floating, + "Altitudes are absolute and not clamped to ground", + "0", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr + }, + { + "extrude", &opt_extrude, + "Draw extrusion line from trackpoint to ground", + "0", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr + }, + { + "track", &opt_export_track, + "Write KML track (default = 0)", + "0", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr + }, + { + "trackdata", &opt_trackdata, + "Include extended data for trackpoints (default = 1)", + "1", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr + }, + { + "trackdirection", &opt_trackdirection, + "Indicate direction of travel in track icons (default = 0)", + "0", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr + }, + { + "units", &opt_units, + "Units used when writing comments ('s'tatute, 'm'etric,' 'n'autical, 'a'viation)", + "s", ARGTYPE_STRING, ARG_NOMINMAX, nullptr + }, + { + "labels", &opt_labels, + "Display labels on track and routepoints (default = 1)", + "1", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr + }, + { + "max_position_points", &opt_max_position_points, + "Retain at most this number of position points (0 = unlimited)", + "0", ARGTYPE_INT, ARG_NOMINMAX, nullptr + }, + { + "rotate_colors", &opt_rotate_colors, + "Rotate colors for tracks and routes (default automatic)", + nullptr, ARGTYPE_FLOAT, "0", "360", nullptr + }, + { + "prec", &opt_precision, + "Precision of coordinates, number of decimals", + default_precision, ARGTYPE_INT, ARG_NOMINMAX, nullptr + }, + }; + + struct { + float seq{0.0f}; + float step{0.0f}; + gb_color color; + } kml_color_sequencer; + + QList> kml_map = { + {&KmlFormat::wpt_s, cb_start, "/Placemark"}, + {&KmlFormat::wpt_e, cb_end, "/Placemark"}, + {&KmlFormat::wpt_name, cb_cdata, "/Placemark/name"}, + {&KmlFormat::wpt_desc, cb_cdata, "/Placemark/description"}, + {&KmlFormat::wpt_ts_begin, cb_cdata,"/Placemark/TimeSpan/begin"}, + {&KmlFormat::wpt_ts_end, cb_cdata, "/Placemark/TimeSpan/end"}, + {&KmlFormat::wpt_time, cb_cdata, "/Placemark/TimeStamp/when"}, + // Alias for above used in KML 2.0 + {&KmlFormat::wpt_time, cb_cdata, "/Placemark/TimeInstant/timePosition"}, + {&KmlFormat::wpt_coord, cb_cdata, "/Placemark/Point/coordinates"}, + {&KmlFormat::wpt_icon, cb_cdata, "/Placemark/Style/Icon/href"}, + {&KmlFormat::trk_coord, cb_cdata, "/Placemark/MultiGeometry/LineString/coordinates"}, + {&KmlFormat::trk_coord, cb_cdata, "/Placemark/GeometryCollection/LineString/coordinates"}, + {&KmlFormat::trk_coord, cb_cdata, "/Placemark/Polygon/outerBoundaryIs/LinearRing/coordinates"}, + {&KmlFormat::trk_coord, cb_cdata, "/Placemark/LineString/coordinates"}, + {&KmlFormat::gx_trk_s, cb_start, "/Placemark/*gx:Track"}, + {&KmlFormat::gx_trk_e, cb_end, "/Placemark/*gx:Track"}, + {&KmlFormat::gx_trk_when, cb_cdata, "/Placemark/*gx:Track/when"}, + {&KmlFormat::gx_trk_coord, cb_cdata, "/Placemark/*gx:Track/gx:coord"}, + {&KmlFormat::gx_trk_s, cb_start, "/Placemark/Track"}, // KML 2.3 + {&KmlFormat::gx_trk_e, cb_end, "/Placemark/Track"}, // KML 2.3 + {&KmlFormat::gx_trk_when, cb_cdata, "/Placemark/Track/when"}, // KML 2.3 + {&KmlFormat::gx_trk_coord, cb_cdata, "/Placemark/Track/coord"}, // KML 2.3 + {&KmlFormat::gx_trk_s, cb_start, "/Placemark/MultiTrack/Track"}, // KML 2.3 + {&KmlFormat::gx_trk_e, cb_end, "/Placemark/MultiTrack/Track"}, // KML 2.3 + {&KmlFormat::gx_trk_when, cb_cdata, "/Placemark/MultiTrack/Track/when"}, // KML 2.3 + {&KmlFormat::gx_trk_coord, cb_cdata, "/Placemark/MultiTrack/Track/coord"} // KML 2.3 + }; + + static const char* kml_tags_to_ignore[]; + static const char* kml_tags_to_skip[]; + + // The TimeSpan/begin and TimeSpan/end DateTimes: + gpsbabel::DateTime wpt_timespan_begin, wpt_timespan_end; + + static const QString map_templates[]; + + route_head* posn_trk_head{nullptr}; +}; + +#endif // KML_H_INCLUDED_ diff --git a/reference/realtime.kml b/reference/realtime.kml new file mode 100644 index 000000000..bca161447 --- /dev/null +++ b/reference/realtime.kml @@ -0,0 +1,803 @@ + + + + GPS position + + + 1970-01-01T00:00:03.464Z + 1970-01-01T00:10:29.519Z + + -32.056227 + 30.474664 + 1728.780603 + + + + + + + + normal + #track_n + + + highlight + #track_h + + + + + + + + + normal + #multiTrack_n + + + highlight + #multiTrack_h + + + + + + + + + normal + #waypoint_n + + + highlight + #waypoint_h + + + + + + Heart Rate + + + Cadence + + + Temperature + + + Depth + + + + Wpt_hCT + + http://link1.example.com/q31hGd + + 1970-01-01T00:00:29.519Z + + + + -32.051141,30.478722 + + + + Track + #multiTrack + + + 1970-01-01T00:00:03.464Z + 1970-01-01T00:00:04.834Z + 1970-01-01T00:00:06.126Z + 1970-01-01T00:00:07.686Z + 1970-01-01T00:00:09.493Z + 1970-01-01T00:00:12.434Z + 1970-01-01T00:00:14.191Z + 1970-01-01T00:00:16.984Z + 1970-01-01T00:00:18.913Z + 1970-01-01T00:00:20.628Z + 1970-01-01T00:00:22.594Z + 1970-01-01T00:00:23.968Z + 1970-01-01T00:00:25.135Z + 1970-01-01T00:00:26.718Z + 1970-01-01T00:00:28.139Z + 1970-01-01T00:00:29.519Z + -32.061312 30.470605 26.03 + -32.060351 30.471588 + -32.059442 30.471933 30.62 + -32.058752 30.472451 75.70 + -32.058143 30.473278 + -32.057275 30.474104 26.70 + -32.056245 30.474413 72.49 + -32.056020 30.475032 + -32.055280 30.475289 57.64 + -32.054461 30.475367 91.16 + -32.054306 30.475838 + -32.053593 30.476512 77.01 + -32.052896 30.476701 2.66 + -32.052293 30.477107 59.30 + -32.051494 30.477113 + -32.051415 30.477953 30.00 + -32.051141 30.478722 + + + + 245 + 19 + 227 + 97 + 112 + 142 + 222 + 130 + 142 + 118 + 64 + 0 + 186 + 155 + 210 + 0 + 0 + + + 0.0 + 0.0 + 162.6 + 0.0 + 170.7 + 0.0 + 421.8 + 830.1 + 218.1 + 292.2 + 426.2 + 0.0 + 911.3 + 174.5 + 804.5 + 52.5 + 640.0 + + + 25 + 0 + 41 + 244 + 250 + 253 + 33 + 194 + 238 + 0 + 227 + 164 + 246 + 52 + 90 + 53 + 96 + + + 0.0 + 17.8 + 4.4 + 12.7 + 0.0 + 0.0 + 2.2 + 7.9 + 4.7 + 31.9 + 0.0 + 24.3 + 23.2 + 28.0 + 21.8 + 0.0 + 0.0 + + + + + + + Track + + + +Distance 4761.1 ft +Min Alt 8.740 ft +Max Alt 299.085 ft +Min Speed 57.9 mph +Max Speed 156.4 mph +Avg Speed 124.6 mph +Avg Heart Rate 147.1 bpm +Min Heart Rate 25 bpm +Max Heart Rate 253 bpm +Avg Cadence 147.8 rpm +Max Cadence 245 rpm +Start Time1970-01-01T00:00:03.464Z +End Time1970-01-01T00:00:29.519Z +]]> + + + 1970-01-01T00:00:03.464Z + 1970-01-01T00:00:29.519Z + + + Points + + Wpt_QB + + +Longitude: -32.061312 +Latitude: 30.470605 +Altitude: 85.400 ft +Heart rate: 25 +Cadence: 245 +Heading: 317.9 + +]]> + + -32.061312 + 30.470605 + 66 + + #track + + -32.061312,30.470605,26.03 + + + + Wpt_Y8GPuG + + +Longitude: -32.060351 +Latitude: 30.471588 +Cadence: 19 +Temperature: 17.8 +Speed: 146.2 mph +Heading: 40.1 +Time: 1970-01-01T00:00:03.464Z + +]]> + + -32.060351 + 30.471588 + 66 + + + 1970-01-01T00:00:03.464Z + + #track + + -32.060351,30.471588 + + + + Wpt_hTiV + + +Longitude: -32.059442 +Latitude: 30.471933 +Altitude: 100.458 ft +Heart rate: 41 +Cadence: 227 +Temperature: 4.4 +Depth: 533.4 ft +Speed: 155.7 mph +Heading: 66.3 +Time: 1970-01-01T00:00:04.834Z + +]]> + + -32.059442 + 30.471933 + 66 + + + 1970-01-01T00:00:04.834Z + + #track + + -32.059442,30.471933,30.62 + + + + Wpt_Fr0VTZa + + +Longitude: -32.058752 +Latitude: 30.472451 +Altitude: 248.355 ft +Heart rate: 244 +Cadence: 97 +Temperature: 12.7 +Speed: 152.0 mph +Heading: 48.9 +Time: 1970-01-01T00:00:06.126Z + +]]> + + -32.058752 + 30.472451 + 66 + + + 1970-01-01T00:00:06.126Z + + #track + + -32.058752,30.472451,75.70 + + + + Wpt_8f + + +Longitude: -32.058143 +Latitude: 30.473278 +Heart rate: 250 +Cadence: 112 +Depth: 560.0 ft +Speed: 156.4 mph +Heading: 32.4 +Time: 1970-01-01T00:00:07.686Z + +]]> + + -32.058143 + 30.473278 + 66 + + + 1970-01-01T00:00:07.686Z + + #track + + -32.058143,30.473278 + + + + Wpt_R + + +Longitude: -32.057275 +Latitude: 30.474104 +Altitude: 87.606 ft +Heart rate: 253 +Cadence: 142 +Speed: 153.6 mph +Heading: 42.2 +Time: 1970-01-01T00:00:09.493Z + +]]> + + -32.057275 + 30.474104 + 66 + + + 1970-01-01T00:00:09.493Z + + #track + + -32.057275,30.474104,26.70 + + + + ESTIMATED Position + + +Longitude: -32.056245 +Latitude: 30.474413 +Altitude: 237.843 ft +Heart rate: 33 +Cadence: 222 +Temperature: 2.2 +Depth: 1383.8 ft +Speed: 83.3 mph +Heading: 70.8 +Time: 1970-01-01T00:00:12.434Z + +]]> + + -32.056245 + 30.474413 + 66 + + + 1970-01-01T00:00:12.434Z + + #track + + -32.056245,30.474413,72.49 + + + + Wpt_8JwVPYQ + + +Longitude: -32.056020 +Latitude: 30.475032 +Heart rate: 194 +Cadence: 130 +Temperature: 7.9 +Depth: 2723.3 ft +Speed: 91.8 mph +Heading: 17.4 +Time: 1970-01-01T00:00:14.191Z + +]]> + + -32.056020 + 30.475032 + 66 + + + 1970-01-01T00:00:14.191Z + + #track + + -32.056020,30.475032 + + + + Wpt_e + + +Longitude: -32.055280 +Latitude: 30.475289 +Altitude: 189.100 ft +Heart rate: 238 +Cadence: 142 +Temperature: 4.7 +Depth: 715.4 ft +Speed: 57.9 mph +Heading: 68.0 +Time: 1970-01-01T00:00:16.984Z + +]]> + + -32.055280 + 30.475289 + 66 + + + 1970-01-01T00:00:16.984Z + + #track + + -32.055280,30.475289,57.64 + + + + ESTIMATED Position + + +Longitude: -32.054461 +Latitude: 30.475367 +Altitude: 299.085 ft +Cadence: 118 +Temperature: 31.9 +Depth: 958.6 ft +Speed: 91.8 mph +Heading: 83.7 +Time: 1970-01-01T00:00:18.913Z + +]]> + + -32.054461 + 30.475367 + 66 + + + 1970-01-01T00:00:18.913Z + + #track + + -32.054461,30.475367,91.16 + + + + ESTIMATED Position + + +Longitude: -32.054306 +Latitude: 30.475838 +Heart rate: 227 +Cadence: 64 +Depth: 1398.4 ft +Speed: 71.0 mph +Heading: 15.8 +Time: 1970-01-01T00:00:20.628Z + +]]> + + -32.054306 + 30.475838 + 66 + + + 1970-01-01T00:00:20.628Z + + #track + + -32.054306,30.475838 + + + + Wpt_4 + + +Longitude: -32.053593 +Latitude: 30.476512 +Altitude: 252.661 ft +Heart rate: 164 +Temperature: 24.3 +Speed: 115.5 mph +Heading: 42.4 +Time: 1970-01-01T00:00:22.594Z + +]]> + + -32.053593 + 30.476512 + 66 + + + 1970-01-01T00:00:22.594Z + + #track + + -32.053593,30.476512,77.01 + + + + Wpt_25dSc2Mb + + +Longitude: -32.052896 +Latitude: 30.476701 +Altitude: 8.740 ft +Heart rate: 246 +Cadence: 186 +Temperature: 23.2 +Depth: 2989.8 ft +Speed: 114.1 mph +Heading: 72.5 +Time: 1970-01-01T00:00:23.968Z + +]]> + + -32.052896 + 30.476701 + 66 + + + 1970-01-01T00:00:23.968Z + + #track + + -32.052896,30.476701,2.66 + + + + Wpt_iHp8 + + +Longitude: -32.052293 +Latitude: 30.477107 +Altitude: 194.545 ft +Heart rate: 52 +Cadence: 155 +Temperature: 28.0 +Depth: 572.3 ft +Speed: 140.7 mph +Heading: 52.0 +Time: 1970-01-01T00:00:25.135Z + +]]> + + -32.052293 + 30.477107 + 66 + + + 1970-01-01T00:00:25.135Z + + #track + + -32.052293,30.477107,59.30 + + + + Wpt_lFMiR + + +Longitude: -32.051494 +Latitude: 30.477113 +Heart rate: 90 +Cadence: 210 +Temperature: 21.8 +Depth: 2639.6 ft +Speed: 108.3 mph +Heading: 89.5 +Time: 1970-01-01T00:00:26.718Z + +]]> + + -32.051494 + 30.477113 + 66 + + + 1970-01-01T00:00:26.718Z + + #track + + -32.051494,30.477113 + + + + Wpt_k2lz15k + + +Longitude: -32.051415 +Latitude: 30.477953 +Altitude: 98.438 ft +Heart rate: 53 +Depth: 172.3 ft +Speed: 147.6 mph +Heading: 4.6 +Time: 1970-01-01T00:00:28.139Z + +]]> + + -32.051415 + 30.477953 + 66 + + + 1970-01-01T00:00:28.139Z + + #track + + -32.051415,30.477953,30.00 + + + + Wpt_hCT + + +Longitude: -32.051141 +Latitude: 30.478722 +Heart rate: 96 +Depth: 2099.9 ft +Speed: 145.1 mph +Heading: 17.1 +Time: 1970-01-01T00:00:29.519Z + +]]> + + -32.051141 + 30.478722 + 66 + + + 1970-01-01T00:00:29.519Z + + #track + + -32.051141,30.478722 + + + + + Path + #lineStyle + + 1 + +-32.061312,30.470605,26.03 +-32.060351,30.471588 +-32.059442,30.471933,30.62 +-32.058752,30.472451,75.70 +-32.058143,30.473278 +-32.057275,30.474104,26.70 +-32.056245,30.474413,72.49 +-32.056020,30.475032 +-32.055280,30.475289,57.64 +-32.054461,30.475367,91.16 +-32.054306,30.475838 +-32.053593,30.476512,77.01 +-32.052896,30.476701,2.66 +-32.052293,30.477107,59.30 +-32.051494,30.477113 +-32.051415,30.477953,30.00 +-32.051141,30.478722 + + + + + + diff --git a/testo.d/kml.test b/testo.d/kml.test index 5a1d2fcfc..2863b86bd 100644 --- a/testo.d/kml.test +++ b/testo.d/kml.test @@ -65,6 +65,10 @@ compare ${REFERENCE}/track/tracks~gpx.kml ${TMPDIR}/tracks~gpx.kml gpsbabel -i kml -f ${REFERENCE}/track/Placemark-Track-1.kml -o gpx -F ${TMPDIR}/Placemark-Track-1~kml.gpx compare ${REFERENCE}/track/Placemark-Track-1~kml.gpx ${TMPDIR}/Placemark-Track-1~kml.gpx +# kml realtime writer +gpsbabel -T -i random,points=20,seed=33,nodelay -f dummy -o kml,track -F ${TMPDIR}/realtime.kml +compare ${REFERENCE}/realtime.kml ${TMPDIR}/realtime.kml + if [ "${RUNNINGVALGRIND}" != "0" ]; then set -e if which xmllint > /dev/null; diff --git a/vecs.h b/vecs.h index e482c4c2f..403e4d052 100644 --- a/vecs.h +++ b/vecs.h @@ -35,10 +35,10 @@ #include "ggv_bin.h" #include "globalsat_sport.h" #include "gpx.h" +#include "kml.h" #include "legacyformat.h" #include "lowranceusr.h" #include "mynav.h" -#include "qstarz_bl_1000.h" #include "nmea.h" #include "qstarz_bl_1000.h" #include "random.h" @@ -58,7 +58,6 @@ extern ff_vecs_t mapsend_vecs; extern ff_vecs_t mps_vecs; extern ff_vecs_t ozi_vecs; extern ff_vecs_t pcx_vecs; -extern ff_vecs_t kml_vecs; #if MAXIMAL_ENABLED extern ff_vecs_t gpsutil_vecs; extern ff_vecs_t holux_vecs; @@ -281,7 +280,7 @@ private: NmeaFormat nmea_fmt; LegacyFormat ozi_fmt {ozi_vecs}; LegacyFormat pcx_fmt {pcx_vecs}; - LegacyFormat kml_fmt {kml_vecs}; + KmlFormat kml_fmt; #if MAXIMAL_ENABLED LegacyFormat gpsutil_fmt {gpsutil_vecs}; LowranceusrFormat lowranceusr_fmt; -- 2.30.2